﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Servicios;
using Dominio;
using System.IO;


namespace Watcher
{
    public class FileSystemWatcherListener
    {
        private static FileSystemWatcherListener instancia = new FileSystemWatcherListener();
        IFileSystemElementService FileSystemService = FabricaDeServicios.ObtenerInstancia().ObtenerFileSystemElementService();
        public List<FileSystemWatcher> Watchers { get; set; }



        private FileSystemWatcherListener() : base() 
        { 
            Watchers = new List<FileSystemWatcher>();
        }

        public static FileSystemWatcherListener ObtenerInstancia()
        {
            return instancia;
        }

        

        public void RegistrarFileSystem(FileSystemElement fse)
        {
            RegistrarWatcher(fse);
            FileSystemService.Registrar(fse);
        }
        void Cambio(object source, FileSystemEventArgs fsea)
        {
            int pos = fsea.FullPath.LastIndexOf(@"\");
            string ruta = fsea.FullPath.Substring(0, pos);
            FileSystemElement fileSystemElement = this.FileSystemService.ObtenerPorRuta(ruta+"/"+fsea.Name);
            try
            {
                FileInfo file = new FileInfo(fsea.FullPath);
                this.ActualizarSegunCambio(fileSystemElement, fsea);
                this.FileSystemService.Modificar(fileSystemElement);
            }
            catch (Exception noArchivo) { }
            //Cambio fecha o atributos, actualizar el indice
            //fsea.FullPath;
            //fsea.Name;
            //fsea.ChangeType;
        }
        void Nuevo(object source, FileSystemEventArgs fsea)
        {
            Archivo archivo = new Archivo();
            archivo.Nombre = fsea.Name;
            int pos = fsea.FullPath.LastIndexOf(@"\");
            archivo.Ruta = fsea.FullPath.Substring(0, pos);
            FileInfo fileInfo = new FileInfo(fsea.FullPath);
            //archivo.CarpetaPadre = (Carpeta)this.fileSistemService.ObtenerPorRuta(fileInfo.DirectoryName);
            //archivo.Extension = fileInfo.Extension;
            archivo.Fecha = fileInfo.CreationTime;
            archivo.Tamanio = fileInfo.Length;
            this.FileSystemService.Registrar(archivo);
            //Nuevo Archivo, actualizar el indice
            //fsea.FullPath;
            //fsea.Name;
            //fsea.ChangeType;
        }
        void Eliminado(object source, FileSystemEventArgs fsea)
        {
            int pos = fsea.FullPath.LastIndexOf(@"\");
            string ruta = fsea.FullPath.Substring(0, pos);
            FileSystemElement fse = this.FileSystemService.ObtenerPorRuta(ruta+"/"+fsea.Name);
            this.FileSystemService.Borrar(fse);
            //Archivo eliminado, actualizar el indice
            //fsea.FullPath;
            //fsea.Name;
            //fsea.ChangeType;
        }
        //---------------------------------------------------------------

        void Renombre(Object source, RenamedEventArgs rea)
        {
            int posOld = rea.OldFullPath.LastIndexOf(@"\");
            string ruta = rea.FullPath.Substring(0, posOld);
            FileSystemElement fileSystemElement = this.FileSystemService.ObtenerPorRuta(ruta + "/" + rea.OldName);
            fileSystemElement.Nombre = rea.Name;
            this.FileSystemService.Modificar(fileSystemElement);
            //Renombrado del archivo, actualizar el indice
            //rea.ChangeType.ToString();
            //rea.OldFullPath;
            //rea.FullPath;
        }
        void BorrarFileSystem(string path)
        {
            FileSystemWatcher borrar = new FileSystemWatcher(path);
            foreach (FileSystemWatcher w in Watchers)
            {
                if (w.Path == borrar.Path)
                {
                    Watchers.Remove(w);
                    //actualizar indices
                }
            }

        }

        private void ActualizarSegunCambio(FileSystemElement fse, FileSystemEventArgs fsea) 
        {
            switch (fsea.ChangeType) 
            {
                case WatcherChangeTypes.Renamed: 
                    fse.Nombre = fsea.Name;
                    break;

                case WatcherChangeTypes.Changed: 
                    FileInfo fileInfo = new FileInfo(fsea.FullPath);
                    fse.Fecha = fileInfo.LastWriteTime;
                    ((Archivo)fse).Tamanio = fileInfo.Length;
                    
                    break;

                default: break;
            }
        }

        void FileSystemAdd() { }

        void FileSystemUpdate() { }

        void FileSystemRemove() { }

        public void Actualizar(FileSystemElement fselem, DirectoryInfo dir)
        {
            fselem.Nombre = dir.Name;
            fselem.Fecha = dir.LastWriteTime;
            FileSystemService.Modificar(fselem);
        }
        public void Actualizar(FileSystemElement fselem, FileInfo file)
        {
            fselem.Nombre = file.Name;
            fselem.Fecha = file.LastWriteTime;
            ((Archivo)fselem).Tamanio = file.Length;
            FileSystemService.Modificar((Archivo)fselem);
        }

        public void Agregar(FileInfo file)
        {
            Archivo fileSystemElement = new Archivo();
            fileSystemElement.Ruta = ObtenerRuta(file.FullName);
            fileSystemElement.Nombre = file.Name;
            fileSystemElement.Fecha = file.LastWriteTime;
            ((Archivo)fileSystemElement).Tamanio = file.Length;
            RegistrarFileSystem(fileSystemElement);
        }

        public void Agregar(DirectoryInfo dir)
        {
            FileSystemElement fileSystemElement = new FileSystemElement();
            string ruta = ObtenerRuta(dir.FullName);
            fileSystemElement.Ruta = ruta;
            fileSystemElement.Nombre = dir.Name;
            fileSystemElement.Fecha = dir.LastWriteTime;
            RegistrarFileSystem(fileSystemElement);
            FileSystemInfo[] files = dir.GetFileSystemInfos();
            foreach (FileSystemInfo f in files)
            {
                if (f is FileInfo)
                {
                    Agregar((FileInfo)f);
                }
                else
                {
                    Agregar((DirectoryInfo)f);
                }
            }
        }
        private string ObtenerRuta(string p)
        {
            string fullPath = p.Replace("\\", "/");
            int pos = fullPath.LastIndexOf("/");
            return fullPath.Substring(0, pos);
        }

        public void Borrar(FileSystemElement fselem)
        {
            foreach (FileSystemWatcher fsw in Watchers)
            {
                if (fsw.Path == fselem.Ruta + "/" + fselem.Nombre)
                {
                    Watchers.Remove(fsw);
                    break;
                }
            }
            FileSystemService.Borrar(fselem);
        }

        public void ActualizarBorrar(List<FileSystemElement> fselems)
        {
            foreach (FileSystemElement fselem in fselems)
            {
                Servicios.IndexServiceLuceneImpl.ObtenerInstancia().AgregarIndice(fselem);
                DirectoryInfo dir = new DirectoryInfo(fselem.Ruta + "/" + fselem.Nombre);
                if (dir.Exists)
                {
                    ObtenerInstancia().Actualizar(fselem, dir);
                }
                else
                {
                    if (fselem is Archivo)
                    {
                        FileInfo file = new FileInfo(fselem.Ruta + "/" + fselem.Nombre);
                        if (file.Exists)
                            ObtenerInstancia().Actualizar(fselem, file);
                        else
                            ObtenerInstancia().Borrar(fselem);
                    }
                    else
                    {
                        ObtenerInstancia().Borrar(fselem);
                    }
                }
            }
        }

        public void AgregarNuevos(List<FileSystemElement> fsdirs)
        {
            foreach (FileSystemElement fs in fsdirs)
            {
                DirectoryInfo dir = new DirectoryInfo(fs.Ruta + "/" + fs.Nombre);
                FileSystemInfo [] fsinfos = dir.GetFileSystemInfos();
                foreach ( FileSystemInfo fsinfo in fsinfos)
                {
                    string ruta = ObtenerRuta(fsinfo.FullName);
                    FileSystemElement elem = FileSystemService.ObtenerPorRuta(ruta+"/"+fsinfo.Name);
                    if (elem == null)
                    {
                        if (fsinfo is DirectoryInfo)
                        {
                            Agregar((DirectoryInfo)fsinfo);
                        }
                        else
                        {
                            Agregar((FileInfo)fsinfo);
                        }
                    }
                }                
            }
        }

        public void CargarFileSystems(List<FileSystemElement> fsdirs)
        {
            foreach (FileSystemElement fe in fsdirs)
            {
                RegistrarWatcher(fe);
            }
        }

        private void RegistrarWatcher(FileSystemElement fse)
        {
            try
            {
                FileSystemWatcher watcher = new FileSystemWatcher(fse.Ruta + "/" + fse.Nombre);
                watcher.NotifyFilter = NotifyFilters.FileName |
                                       NotifyFilters.Attributes |
                                       NotifyFilters.LastWrite;

                watcher.Changed += new FileSystemEventHandler(Cambio);
                watcher.Created += new FileSystemEventHandler(Nuevo);
                watcher.Deleted += new FileSystemEventHandler(Eliminado);
                watcher.Renamed += new RenamedEventHandler(Renombre);
                watcher.IncludeSubdirectories = true;
                watcher.EnableRaisingEvents = true;    //trigger event logging
                Watchers.Add(watcher);
            }
            catch (ArgumentException noEsDir)
            {
                //Si es un archivo,solo lo agrego a  los indices
                //solo entra en el registrarFilesystemelement
            }
        }
    }
}
